@juangcarmona/lighthouse 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -16,6 +16,8 @@ Use `lighthouse reconcile` to preview missing Lighthouse-managed paths in an exi
16
16
 
17
17
  Use `lighthouse upgrade` to preview a versioned foundation update. The command is dry-run by default, accepts `--scope harness`, `--scope sdlc` or `--scope all`, preserves customized projections and stops when ownership evidence is missing. Add `--apply` only after reviewing the plan, then run `lighthouse verify`.
18
18
 
19
+ Agent workflows are opt-in through the SDLC package installer. Initialization does not create an entire worker catalog. Upgrading an older adoption releases workflow ownership while preserving the existing files for explicit review and cleanup.
20
+
19
21
  The harness package owns the baseline command and verification contract. The SDLC package owns lifecycle skills, commands, metacommands, routes and gates, and `lighthouse init` records its selected version in `.lighthouse/sdlc.json`. The UI package owns visual tokens, native recipes, `DESIGN.md` and its agent skill. Selecting `visual-theme` projects the guide and skill into the adopter and enables its deterministic guardrail. Stack profiles own their source templates, guidance and checks; the .NET creator is the packaged entry point for the selected backend profile.
20
22
 
21
23
  ProductShape, ArcShape and OpenSpec keep their own files and commands. Add them through their supported integration routes when the project needs them, and keep their versions and managed paths visible in the project configuration. In an existing repository, run `lighthouse authorities --adopt-existing` to validate the current ProductShape and ArcShape surfaces without running either initializer; review the dry-run plan and add `--apply` only to record both exact versions in Lighthouse-owned configuration.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juangcarmona/lighthouse",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "type": "module",
5
5
  "description": "Lighthouse adoption and verification CLI",
6
6
  "repository": {
@@ -23,7 +23,7 @@
23
23
  "@arcshape/cli": "0.1.6",
24
24
  "@juangcarmona/lighthouse-ui": "0.2.0",
25
25
  "@prodshape/cli": "0.23.0",
26
- "@juangcarmona/lighthouse-sdlc": "0.11.0"
26
+ "@juangcarmona/lighthouse-sdlc": "0.12.0"
27
27
  },
28
28
  "files": [
29
29
  "src/lighthouse.mjs",
@@ -1,6 +1,5 @@
1
1
  #!/usr/bin/env node
2
2
  import { lstat, mkdir, mkdtemp, readFile, readdir, rename, rm, rmdir, writeFile } from 'node:fs/promises';
3
- import { createHash } from 'node:crypto';
4
3
  import { dirname, join, resolve } from 'node:path';
5
4
  import { fileURLToPath } from 'node:url';
6
5
  import { createRequire } from 'node:module';
@@ -10,23 +9,6 @@ const templateRoot = resolve(packageRoot, 'assets/dotnet-template');
10
9
  const cliPackage = JSON.parse(await readFile(resolve(packageRoot, 'package.json'), 'utf8'));
11
10
  const require = createRequire(import.meta.url);
12
11
  const sdlcContract = JSON.parse(await readFile(require.resolve('@juangcarmona/lighthouse-sdlc/contract.json'), 'utf8'));
13
- const sdlcPackageRoot = dirname(require.resolve('@juangcarmona/lighthouse-sdlc/contract.json'));
14
- const sdlcWorkflowPaths = [
15
- '.github/workflows/lighthouse-sdlc-router.yml',
16
- '.github/workflows/lighthouse-sdlc-triage.md',
17
- '.github/workflows/lighthouse-sdlc-triage.lock.yml',
18
- '.github/workflows/lighthouse-sdlc-refine.md',
19
- '.github/workflows/lighthouse-sdlc-refine.lock.yml',
20
- '.github/workflows/lighthouse-sdlc-propose.md',
21
- '.github/workflows/lighthouse-sdlc-propose.lock.yml',
22
- '.github/workflows/lighthouse-sdlc-implement.md',
23
- '.github/workflows/lighthouse-sdlc-implement.lock.yml',
24
- '.github/workflows/lighthouse-sdlc-integrate.md',
25
- '.github/workflows/lighthouse-sdlc-integrate.lock.yml',
26
- '.github/workflows/lighthouse-sdlc-review.md',
27
- '.github/workflows/lighthouse-sdlc-review.lock.yml',
28
- '.github/workflows/shared/lighthouse-sdlc-stage.md',
29
- ];
30
12
  const args = process.argv.slice(2);
31
13
  const target = args[0] ? resolve(args[0]) : null;
32
14
  const name = args.includes('--name') ? args[args.indexOf('--name') + 1] : null;
@@ -109,7 +91,7 @@ try {
109
91
  product: name, application: { shape: 'backend', stacks: ['dotnet'] }, profiles: ['dotnet-backend'],
110
92
  integrations: { productShape: null, arcShape: null },
111
93
  packages: { sdlc: { name: '@juangcarmona/lighthouse-sdlc', version: sdlcContract.version } },
112
- managed: ['LIGHTHOUSE.md', 'scripts/lighthouse-guard.mjs', '.husky/commit-msg', '.husky/pre-push', '.github/workflows/lighthouse.yml', '.lighthouse/sdlc.json', ...sdlcWorkflowPaths, '.agents/skills/lighthouse-init/SKILL.md', '.lighthouse/upgrade.json', '.lighthouse/commands.json', 'docs/profiles/dotnet/README.md', 'docs/profiles/dotnet/adoption.md', 'docs/profiles/dotnet/architecture.md', 'docs/profiles/dotnet/guardrails.md', 'docs/profiles/dotnet/practices.md', 'docs/profiles/dotnet/testing-and-development.md', 'docs/profiles/dotnet/use-cases.md', '.agents/skills/lighthouse-dotnet/SKILL.md'],
94
+ managed: ['LIGHTHOUSE.md', 'scripts/lighthouse-guard.mjs', '.husky/commit-msg', '.husky/pre-push', '.github/workflows/lighthouse.yml', '.lighthouse/sdlc.json', '.agents/skills/lighthouse-init/SKILL.md', '.lighthouse/upgrade.json', '.lighthouse/commands.json', 'docs/profiles/dotnet/README.md', 'docs/profiles/dotnet/adoption.md', 'docs/profiles/dotnet/architecture.md', 'docs/profiles/dotnet/guardrails.md', 'docs/profiles/dotnet/practices.md', 'docs/profiles/dotnet/testing-and-development.md', 'docs/profiles/dotnet/use-cases.md', '.agents/skills/lighthouse-dotnet/SKILL.md'],
113
95
  repository: {
114
96
  layout: ['src', 'docs', 'infra', 'scripts'], publicSite: { enabled: true },
115
97
  conventions: { commits: 'conventional', branchPattern: '^(feat|fix|docs|refactor|test|chore|build|ci|perf|revert)/[1-9]\\d*-[a-z0-9]+(?:-[a-z0-9]+)*$' },
@@ -126,14 +108,6 @@ try {
126
108
 
127
109
  const initialized = (await import('node:child_process')).spawnSync(process.execPath, [resolve(stage, 'scripts/lighthouse.mjs'), 'init'], { cwd: stage, encoding: 'utf8' });
128
110
  if (initialized.status !== 0) throw new Error(initialized.stderr || 'Projection generation failed.');
129
- await copy(resolve(sdlcPackageRoot, 'workflows'), resolve(stage, '.github/workflows'));
130
- const upgradePath = resolve(stage, '.lighthouse/upgrade.json');
131
- const upgrade = JSON.parse(await readFile(upgradePath, 'utf8'));
132
- for (const path of sdlcWorkflowPaths) {
133
- const content = await readFile(resolve(stage, path), 'utf8');
134
- upgrade.files[path] = { hash: createHash('sha256').update(content).digest('hex'), scope: 'sdlc' };
135
- }
136
- await writeFile(upgradePath, `${JSON.stringify(upgrade, null, 2)}\n`);
137
111
  if (existing) await rmdir(target);
138
112
  await rename(stage, target);
139
113
  process.stdout.write(`Created ${name} in ${target}. Run dotnet restore ${name}.slnx, then npm run verify.\n`);
@@ -41,22 +41,6 @@ const upgradeStatePath = resolve(root, upgradeStateRelative);
41
41
  const layout = ['src', 'docs', 'infra', 'scripts'];
42
42
  const sdlcPackage = { name: '@juangcarmona/lighthouse-sdlc', version: installedPackageVersion('@juangcarmona/lighthouse-sdlc', 'contract.json') ?? '0.1.0' };
43
43
  const uiPackage = { name: '@juangcarmona/lighthouse-ui', version: installedPackageVersion('@juangcarmona/lighthouse-ui', 'DESIGN.md') ?? '0.1.0' };
44
- const sdlcWorkflowPaths = Object.freeze([
45
- '.github/workflows/lighthouse-sdlc-router.yml',
46
- '.github/workflows/lighthouse-sdlc-triage.md',
47
- '.github/workflows/lighthouse-sdlc-triage.lock.yml',
48
- '.github/workflows/lighthouse-sdlc-refine.md',
49
- '.github/workflows/lighthouse-sdlc-refine.lock.yml',
50
- '.github/workflows/lighthouse-sdlc-propose.md',
51
- '.github/workflows/lighthouse-sdlc-propose.lock.yml',
52
- '.github/workflows/lighthouse-sdlc-implement.md',
53
- '.github/workflows/lighthouse-sdlc-implement.lock.yml',
54
- '.github/workflows/lighthouse-sdlc-integrate.md',
55
- '.github/workflows/lighthouse-sdlc-integrate.lock.yml',
56
- '.github/workflows/lighthouse-sdlc-review.md',
57
- '.github/workflows/lighthouse-sdlc-review.lock.yml',
58
- '.github/workflows/shared/lighthouse-sdlc-stage.md',
59
- ]);
60
44
  const managedPaths = [
61
45
  projection,
62
46
  'scripts/lighthouse-guard.mjs',
@@ -64,7 +48,6 @@ const managedPaths = [
64
48
  '.husky/pre-push',
65
49
  '.github/workflows/lighthouse.yml',
66
50
  '.lighthouse/sdlc.json',
67
- ...sdlcWorkflowPaths,
68
51
  '.agents/skills/lighthouse-init/SKILL.md',
69
52
  upgradeStateRelative,
70
53
  '.lighthouse/commands.json',
@@ -102,7 +85,7 @@ function managedPathsFor(manifest) {
102
85
  function upgradeScopesFor(manifest) {
103
86
  return {
104
87
  harness: [projection, 'scripts/lighthouse-guard.mjs', '.husky/commit-msg', '.husky/pre-push', '.github/workflows/lighthouse.yml', '.agents/skills/lighthouse-init/SKILL.md', ...((manifest?.profiles ?? []).flatMap((profile) => profileManagedPaths[profile] ?? []))],
105
- sdlc: ['.lighthouse/sdlc.json', '.lighthouse/commands.json', ...sdlcWorkflowPaths],
88
+ sdlc: ['.lighthouse/sdlc.json', '.lighthouse/commands.json'],
106
89
  };
107
90
  }
108
91
 
@@ -111,18 +94,6 @@ function packageAsset(packageName, entry) {
111
94
  return readFileSync(path, 'utf8');
112
95
  }
113
96
 
114
- function sdlcWorkflowFiles() {
115
- const files = {};
116
- for (const target of sdlcWorkflowPaths) {
117
- try {
118
- files[target] = packageAsset('@juangcarmona/lighthouse-sdlc', `workflows/${target.slice('.github/workflows/'.length)}`);
119
- } catch {
120
- return {};
121
- }
122
- }
123
- return files;
124
- }
125
-
126
97
  function profileAssets(manifest) {
127
98
  const assets = {};
128
99
  if ((manifest?.profiles ?? []).includes('visual-theme')) {
@@ -457,7 +428,6 @@ function managedFiles(manifest) {
457
428
  '.agents/skills/lighthouse-init/SKILL.md': readFileSync(new URL('../assets/skills/init/SKILL.md', import.meta.url), 'utf8'),
458
429
  };
459
430
  Object.assign(files, profileAssets(manifest));
460
- Object.assign(files, sdlcWorkflowFiles());
461
431
  const controlPlane = sdlcControlPlane(manifest);
462
432
  if (controlPlane) files['.lighthouse/sdlc.json'] = controlPlane;
463
433
  files['.lighthouse/commands.json'] = commandAliasProjectionText(commandAliasesProjection());
@@ -542,6 +512,10 @@ function classifyUpgradeFile(path, current, expected, recordedHash) {
542
512
  return { classification: 'conflicting', action: 'review', reason: 'ownership is not recorded for this path' };
543
513
  }
544
514
 
515
+ function isOptionalWorkflow(path) {
516
+ return /^\.github\/workflows\/lighthouse-sdlc-(router\.yml|(?:triage|refine|propose|implement|integrate|review)\.(?:md|lock\.yml))$/.test(path) || path === '.github/workflows/shared/lighthouse-sdlc-stage.md';
517
+ }
518
+
545
519
  async function planUpgrade(manifest, scopes) {
546
520
  const state = await readUpgradeState();
547
521
  const problems = stateProblems(state);
@@ -557,6 +531,9 @@ async function planUpgrade(manifest, scopes) {
557
531
  const result = classifyUpgradeFile(path, current, expected, recordedHash);
558
532
  changes.push({ path, scope: Object.entries(upgradeScopes).find(([, values]) => values.includes(path))?.[0], ...result });
559
533
  }
534
+ if (scopes.has('sdlc') && manifest.managed.some(isOptionalWorkflow)) {
535
+ changes.push({ path: 'lighthouse.json', scope: 'sdlc', classification: 'adoptable', action: 'update', reason: 'release optional workflow ownership; preserve all workflow files' });
536
+ }
560
537
  if (state?.baseline && state.baseline !== manifest.baseline) {
561
538
  changes.unshift({ path: 'baseline', scope: 'harness', classification: 'breaking', action: 'review', reason: `baseline ${state.baseline} is not compatible with ${manifest.baseline}` });
562
539
  }
@@ -688,7 +665,11 @@ function validate(manifest) {
688
665
  const preSemanticWithoutUpgrade = Array.isArray(manifest.managed)
689
666
  && manifest.managed.length === preSemanticWithoutUpgradePaths.length
690
667
  && manifest.managed.every((entry, index) => entry === preSemanticWithoutUpgradePaths[index]);
691
- if (!legacyManaged && !profilelessManaged && !previousCompleteManaged && !completeManaged && !preSemanticManaged && !preSemanticWithoutUpgrade) {
668
+ // Earlier foundations installed every workflow. Accept that ownership inventory during upgrade;
669
+ // the foundation now leaves those files to explicit workflow adoption and never deletes them.
670
+ const withoutOldWorkflows = Array.isArray(manifest.managed) ? manifest.managed.filter(path => !isOptionalWorkflow(path)) : [];
671
+ const previousWorkflowManaged = withoutOldWorkflows.length === expectedManagedPaths.length && withoutOldWorkflows.every((path, index) => path === expectedManagedPaths[index]);
672
+ if (!previousWorkflowManaged && !legacyManaged && !profilelessManaged && !previousCompleteManaged && !completeManaged && !preSemanticManaged && !preSemanticWithoutUpgrade) {
692
673
  problems.push(`managed must be ${legacyManagedPaths.join(', ')}, the previous Lighthouse guardrail set or the selected profile set`);
693
674
  }
694
675
  if ('repository' in manifest) {
@@ -990,7 +971,10 @@ async function applyUpgrade(manifest, plan) {
990
971
  for (const change of updates) {
991
972
  const target = resolve(root, change.path);
992
973
  await mkdir(dirname(target), { recursive: true });
993
- await writeTextAtomically(target, files[change.path]);
974
+ const content = change.path === 'lighthouse.json'
975
+ ? `${JSON.stringify({ ...manifest, managed: manifest.managed.filter(path => !isOptionalWorkflow(path)) }, null, 2)}\n`
976
+ : files[change.path];
977
+ await writeTextAtomically(target, content);
994
978
  }
995
979
  const next = upgradeState(manifest, files, { ...previous, lastAppliedMigration: `upgrade-${cliPackage.version}` });
996
980
  for (const path of Object.keys(next.files)) {