its-magic 0.1.2-9 → 0.1.3-0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1602 -755
- package/bin/its-magic.js +121 -11
- package/bin/postinstall.js +93 -0
- package/installer.ps1 +759 -0
- package/installer.py +1036 -0
- package/installer.sh +649 -0
- package/package.json +18 -14
- package/scripts/check_intake_template_parity.py +312 -0
- package/scripts/doc_profile_lib.py +415 -0
- package/scripts/guard_installer_publish.py +88 -0
- package/scripts/intake_bug_routing_guard.py +67 -0
- package/scripts/intake_evidence_lib.py +802 -0
- package/scripts/intake_evidence_validate.py +73 -0
- package/scripts/materialize_codebase_map.py +184 -0
- package/scripts/remote_config_summary.py +243 -0
- package/template/.cursor/agents/curator.mdc +35 -0
- package/template/.cursor/agents/dev.mdc +29 -0
- package/template/.cursor/agents/po.mdc +141 -0
- package/template/.cursor/agents/qa.mdc +28 -0
- package/template/.cursor/agents/release.mdc +28 -0
- package/template/.cursor/agents/security.mdc +98 -0
- package/template/.cursor/agents/tech-lead.mdc +57 -0
- package/template/.cursor/commands/architecture.md +127 -0
- package/template/.cursor/commands/ask.md +55 -0
- package/template/.cursor/commands/auto.md +533 -0
- package/template/.cursor/commands/discovery.md +47 -0
- package/template/.cursor/commands/execute.md +343 -0
- package/template/.cursor/commands/intake.md +323 -0
- package/template/.cursor/commands/map-codebase.md +46 -0
- package/template/.cursor/commands/memory-audit.md +175 -0
- package/template/.cursor/commands/milestone-complete.md +51 -0
- package/template/.cursor/commands/milestone-start.md +59 -0
- package/template/.cursor/commands/pause.md +64 -0
- package/{.cursor/commands/gsd-phase-context.md → template/.cursor/commands/phase-context.md} +6 -2
- package/template/.cursor/commands/plan-verify.md +34 -0
- package/template/.cursor/commands/qa.md +226 -0
- package/template/.cursor/commands/quick.md +41 -0
- package/template/.cursor/commands/refresh-context.md +82 -0
- package/template/.cursor/commands/release.md +523 -0
- package/template/.cursor/commands/research.md +49 -0
- package/template/.cursor/commands/resume.md +67 -0
- package/template/.cursor/commands/security-review.md +81 -0
- package/template/.cursor/commands/sprint-plan.md +103 -0
- package/template/.cursor/commands/status-reconcile.md +95 -0
- package/template/.cursor/commands/verify-work.md +152 -0
- package/template/.cursor/dev-environment.json.example +22 -0
- package/{.cursor → template/.cursor}/hooks/README.md +3 -2
- package/{.cursor/hooks/gsd-hook.py → template/.cursor/hooks/hook.py} +15 -6
- package/template/.cursor/hooks.json +26 -0
- package/template/.cursor/remote.json +31 -0
- package/template/.cursor/rules/caveman.mdc +184 -0
- package/template/.cursor/rules/coding-standards.mdc +39 -0
- package/template/.cursor/rules/core.mdc +111 -0
- package/template/.cursor/rules/handoffs.mdc +27 -0
- package/template/.cursor/rules/quality.mdc +49 -0
- package/template/.cursor/scratchpad.local.example.md +323 -0
- package/template/.cursor/scratchpad.md +324 -0
- package/{.cursor/skills/gsd-team → template/.cursor/skills/its-magic}/SKILL.md +2 -2
- package/template/.cursorignore +6 -0
- package/template/.env.example +28 -0
- package/template/.github/workflows/ci.yml +194 -0
- package/{.github → template/.github}/workflows/deploy.yml +5 -2
- package/template/.its-magic-version +1 -0
- package/template/CHANGELOG.md +11 -0
- package/template/README.md +1602 -0
- package/template/decisions/DEC-0001.md +11 -0
- package/template/decisions/DEC-0002.md +11 -0
- package/template/docs/developer/README.md +44 -0
- package/template/docs/engineering/architecture.md +9 -0
- package/template/docs/engineering/artifact-ordering-policy.md +48 -0
- package/template/docs/engineering/artifact-ownership-policy.md +57 -0
- package/template/docs/engineering/auto-orchestration-reference.md +1211 -0
- package/{docs → template/docs}/engineering/codebase-map.md +0 -5
- package/template/docs/engineering/compatibility-report.md +20 -0
- package/template/docs/engineering/compatibility-signals.md +7 -0
- package/template/docs/engineering/component-scope-report.md +16 -0
- package/template/docs/engineering/component-scope.md +19 -0
- package/template/docs/engineering/context/installer-owned-paths.manifest +96 -0
- package/template/docs/engineering/context/readme-section-affinity.json +30 -0
- package/template/docs/engineering/decisions.md +16 -0
- package/template/docs/engineering/legacy-drift-audit.md +21 -0
- package/template/docs/engineering/manifests/components/api-gateway.manifest.yaml +12 -0
- package/template/docs/engineering/manifests/registry.manifest.yaml +20 -0
- package/template/docs/engineering/manifests/repo.manifest.yaml +11 -0
- package/template/docs/engineering/phase-context.md +16 -0
- package/template/docs/engineering/release-targets.json +123 -0
- package/template/docs/engineering/research.md +29 -0
- package/template/docs/engineering/runbook.md +2391 -0
- package/template/docs/engineering/runtime-connectivity.md +81 -0
- package/template/docs/engineering/security-review.md +33 -0
- package/template/docs/engineering/spec-pack/README.md +20 -0
- package/template/docs/engineering/state-archive/README.md +15 -0
- package/template/docs/engineering/state.md +20 -0
- package/template/docs/engineering/status-normalization-report.md +19 -0
- package/template/docs/engineering/token-cost-parity-manifest.md +16 -0
- package/template/docs/engineering/us-0084-remote-e2e.md +44 -0
- package/template/docs/product/acceptance.md +1 -0
- package/template/docs/product/backlog.md +5 -0
- package/template/docs/product/vision.md +11 -0
- package/template/docs/user-guides/README.md +40 -0
- package/{handoffs → template/handoffs}/dev_to_qa.md +0 -3
- package/{handoffs → template/handoffs}/po_to_tl.md +0 -3
- package/{handoffs → template/handoffs}/qa_to_dev.md +1 -2
- package/template/handoffs/release_notes.md +51 -0
- package/template/handoffs/release_queue.md +47 -0
- package/template/handoffs/release_to_dev.md +31 -0
- package/template/handoffs/releases/Sxxxx-release-notes.md +62 -0
- package/template/handoffs/releases/vX.Y.Z-release-notes.md.example +21 -0
- package/template/handoffs/resume_brief.md +14 -0
- package/{handoffs → template/handoffs}/tl_to_dev.md +0 -2
- package/template/handoffs/token_cost_runs/README.md +26 -0
- package/template/its_magic/.its-magic-version +1 -0
- package/template/its_magic/README.md +9 -0
- package/template/scripts/auto_outer_driver.py +521 -0
- package/template/scripts/caveman_compress_input.py +903 -0
- package/template/scripts/check_downstream_ci_guard.py +67 -0
- package/template/scripts/check_intake_template_parity.py +312 -0
- package/template/scripts/check_token_cost_parity.py +69 -0
- package/template/scripts/dev_environment_lib.py +601 -0
- package/template/scripts/doc_profile_lib.py +415 -0
- package/template/scripts/downstream_ci_guard_lib.py +222 -0
- package/template/scripts/enforce-triad-hot-surface.py +753 -0
- package/template/scripts/guard_installer_publish.py +88 -0
- package/template/scripts/intake_bug_resume_brief_refresh.py +303 -0
- package/template/scripts/intake_bug_routing_guard.py +67 -0
- package/template/scripts/intake_evidence_lib.py +802 -0
- package/template/scripts/intake_evidence_validate.py +73 -0
- package/template/scripts/materialize_codebase_map.py +184 -0
- package/template/scripts/pack_json_validate.py +130 -0
- package/template/scripts/project_readme_coverage_lib.py +417 -0
- package/template/scripts/readme_feature_coverage_lib.py +608 -0
- package/{scripts → template/scripts}/release-all.sh +27 -4
- package/template/scripts/release_changelog_backfill.py +153 -0
- package/template/scripts/release_changelog_lib.py +544 -0
- package/template/scripts/release_changelog_validate.py +134 -0
- package/template/scripts/remote_config_summary.py +243 -0
- package/template/scripts/sync_push_gates.py +198 -0
- package/template/scripts/token_cost_compare.py +40 -0
- package/template/scripts/token_cost_lib.py +108 -0
- package/template/scripts/uat_probe_lib.py +868 -0
- package/template/scripts/validate-and-push.ps1 +280 -0
- package/template/scripts/validate-and-push.sh +243 -0
- package/template/scripts/validate_doc_profile.py +103 -0
- package/template/scripts/validate_project_readme_coverage.py +151 -0
- package/template/scripts/validate_readme_feature_coverage.py +140 -0
- package/template/sprints/S0001/progress.md +1 -0
- package/template/sprints/S0001/qa-findings.md +9 -0
- package/template/sprints/S0001/release-findings.md +24 -0
- package/template/sprints/S0001/sprint.md +9 -0
- package/template/sprints/S0001/summary.md +7 -0
- package/template/sprints/S0001/tasks.md +1 -0
- package/template/sprints/S0001/uat.json +6 -0
- package/template/sprints/S0001/uat.md +5 -0
- package/template/sprints/quick/Q0001/summary.md +1 -0
- package/{sprints → template/sprints}/quick/Q0001/task.json +0 -1
- package/.cursor/agents/curator.mdc +0 -21
- package/.cursor/agents/dev.mdc +0 -20
- package/.cursor/agents/po.mdc +0 -19
- package/.cursor/agents/qa.mdc +0 -19
- package/.cursor/agents/release.mdc +0 -19
- package/.cursor/agents/tech-lead.mdc +0 -21
- package/.cursor/commands/gsd-architecture.md +0 -29
- package/.cursor/commands/gsd-auto.md +0 -27
- package/.cursor/commands/gsd-discovery.md +0 -27
- package/.cursor/commands/gsd-execute.md +0 -32
- package/.cursor/commands/gsd-intake.md +0 -28
- package/.cursor/commands/gsd-map-codebase.md +0 -25
- package/.cursor/commands/gsd-milestone-complete.md +0 -24
- package/.cursor/commands/gsd-milestone-start.md +0 -26
- package/.cursor/commands/gsd-pause.md +0 -25
- package/.cursor/commands/gsd-plan-verify.md +0 -26
- package/.cursor/commands/gsd-qa.md +0 -28
- package/.cursor/commands/gsd-quick.md +0 -24
- package/.cursor/commands/gsd-refresh-context.md +0 -26
- package/.cursor/commands/gsd-release.md +0 -29
- package/.cursor/commands/gsd-research.md +0 -28
- package/.cursor/commands/gsd-resume.md +0 -26
- package/.cursor/commands/gsd-sprint-plan.md +0 -30
- package/.cursor/commands/gsd-verify-work.md +0 -25
- package/.cursor/hooks.json +0 -26
- package/.cursor/plans/cursor-gsd-team-kit_8cfee9b8.plan.md +0 -57
- package/.cursor/remote.json +0 -18
- package/.cursor/rules/gsd-core.mdc +0 -18
- package/.cursor/rules/gsd-handoffs.mdc +0 -10
- package/.cursor/rules/gsd-quality.mdc +0 -15
- package/.cursor/scratchpad.md +0 -34
- package/.github/workflows/ci.yml +0 -47
- package/decisions/DEC-0001.md +0 -21
- package/decisions/DEC-0002.md +0 -21
- package/docs/engineering/architecture.md +0 -354
- package/docs/engineering/decisions.md +0 -6
- package/docs/engineering/research.md +0 -11
- package/docs/engineering/runbook.md +0 -32
- package/docs/engineering/state.md +0 -33
- package/docs/product/acceptance.md +0 -6
- package/docs/product/backlog.md +0 -7
- package/docs/product/vision.md +0 -46
- package/gsd-installer.ps1 +0 -189
- package/gsd-installer.py +0 -195
- package/gsd-installer.sh +0 -201
- package/handoffs/release_notes.md +0 -14
- package/handoffs/resume_brief.md +0 -8
- package/milestones/M0001/milestone.json +0 -7
- package/milestones/M0001/phases.json +0 -9
- package/milestones/M0001/progress.md +0 -3
- package/milestones/M0001/summary.md +0 -3
- package/scripts/generate-release-notes.ps1 +0 -74
- package/scripts/generate-release-notes.sh +0 -63
- package/scripts/release-all.ps1 +0 -423
- package/sprints/S0001/progress.md +0 -4
- package/sprints/S0001/qa-findings.md +0 -113
- package/sprints/S0001/sprint.md +0 -70
- package/sprints/S0001/summary.md +0 -46
- package/sprints/S0001/tasks.md +0 -35
- package/sprints/S0001/uat.json +0 -8
- package/sprints/S0001/uat.md +0 -8
- package/sprints/quick/Q0001/summary.md +0 -3
- /package/{.cursor/rules/gsd-escalation.mdc → template/.cursor/rules/escalation.mdc} +0 -0
- /package/{.cursor/skills/gsd-team → template/.cursor/skills/its-magic}/templates/acceptance.json +0 -0
- /package/{.cursor/skills/gsd-team → template/.cursor/skills/its-magic}/templates/acceptance.md +0 -0
- /package/{.cursor/skills/gsd-team → template/.cursor/skills/its-magic}/templates/architecture.json +0 -0
- /package/{.cursor/skills/gsd-team → template/.cursor/skills/its-magic}/templates/architecture.md +0 -0
- /package/{.cursor/skills/gsd-team → template/.cursor/skills/its-magic}/templates/decision.json +0 -0
- /package/{.cursor/skills/gsd-team → template/.cursor/skills/its-magic}/templates/decision.md +0 -0
- /package/{.cursor/skills/gsd-team → template/.cursor/skills/its-magic}/templates/handoff.json +0 -0
- /package/{.cursor/skills/gsd-team → template/.cursor/skills/its-magic}/templates/handoff.md +0 -0
- /package/{.cursor/skills/gsd-team → template/.cursor/skills/its-magic}/templates/milestone.json +0 -0
- /package/{.cursor/skills/gsd-team → template/.cursor/skills/its-magic}/templates/phase-context.json +0 -0
- /package/{.cursor/skills/gsd-team → template/.cursor/skills/its-magic}/templates/plan-verify.json +0 -0
- /package/{.cursor/skills/gsd-team → template/.cursor/skills/its-magic}/templates/sprint.json +0 -0
- /package/{.cursor/skills/gsd-team → template/.cursor/skills/its-magic}/templates/sprint.md +0 -0
- /package/{.cursor/skills/gsd-team → template/.cursor/skills/its-magic}/templates/story.json +0 -0
- /package/{.cursor/skills/gsd-team → template/.cursor/skills/its-magic}/templates/story.md +0 -0
- /package/{.cursor/skills/gsd-team → template/.cursor/skills/its-magic}/templates/uat.json +0 -0
- /package/{docs → template/docs}/engineering/context/phase-template.json +0 -0
- /package/{docs → template/docs}/engineering/dependencies.json +0 -0
- /package/{sprints → template/sprints}/S0001/plan-verify.json +0 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Validate intake_evidence JSON bundles (US-0078 / US-0083 / DEC-0060 / DEC-0067).
|
|
4
|
+
|
|
5
|
+
Used by PO workflow preflight and CI fixtures.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import argparse
|
|
11
|
+
import json
|
|
12
|
+
import os
|
|
13
|
+
import sys
|
|
14
|
+
|
|
15
|
+
_SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
16
|
+
_REPO_ROOT = os.path.normpath(os.path.join(_SCRIPT_DIR, ".."))
|
|
17
|
+
if _SCRIPT_DIR not in sys.path:
|
|
18
|
+
sys.path.insert(0, _SCRIPT_DIR)
|
|
19
|
+
|
|
20
|
+
import intake_evidence_lib # noqa: E402
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def main() -> int:
|
|
24
|
+
p = argparse.ArgumentParser(description="Validate intake_evidence JSON (US-0078/US-0083).")
|
|
25
|
+
p.add_argument("--file", help="Path to JSON file containing one intake_evidence object.")
|
|
26
|
+
p.add_argument(
|
|
27
|
+
"--stdin",
|
|
28
|
+
action="store_true",
|
|
29
|
+
help="Read JSON object from stdin.",
|
|
30
|
+
)
|
|
31
|
+
p.add_argument(
|
|
32
|
+
"--self-test",
|
|
33
|
+
action="store_true",
|
|
34
|
+
help="Run library sanity checks and exit.",
|
|
35
|
+
)
|
|
36
|
+
args = p.parse_args()
|
|
37
|
+
|
|
38
|
+
if args.self_test:
|
|
39
|
+
intake_evidence_lib.self_test()
|
|
40
|
+
print("[INTAKE_EVIDENCE_SELF_TEST_OK]")
|
|
41
|
+
return 0
|
|
42
|
+
|
|
43
|
+
raw = ""
|
|
44
|
+
if args.file:
|
|
45
|
+
with open(os.path.abspath(args.file), encoding="utf-8") as f:
|
|
46
|
+
raw = f.read()
|
|
47
|
+
elif args.stdin:
|
|
48
|
+
raw = sys.stdin.read()
|
|
49
|
+
else:
|
|
50
|
+
print("error: specify --file, --stdin, or --self-test", file=sys.stderr)
|
|
51
|
+
return 2
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
bundle = json.loads(raw)
|
|
55
|
+
except json.JSONDecodeError as e:
|
|
56
|
+
print(f"error: invalid JSON: {e}", file=sys.stderr)
|
|
57
|
+
return 2
|
|
58
|
+
|
|
59
|
+
if not isinstance(bundle, dict):
|
|
60
|
+
print("error: root must be a JSON object", file=sys.stderr)
|
|
61
|
+
return 2
|
|
62
|
+
|
|
63
|
+
r = intake_evidence_lib.validate_intake_evidence(bundle)
|
|
64
|
+
if r.ok:
|
|
65
|
+
print("[INTAKE_EVIDENCE_VALIDATION_OK]")
|
|
66
|
+
return 0
|
|
67
|
+
|
|
68
|
+
print(intake_evidence_lib.format_blocked_message(r), file=sys.stderr)
|
|
69
|
+
return 1
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
if __name__ == "__main__":
|
|
73
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Idempotent codebase map bootstrap (US-0082 / DEC-0065).
|
|
3
|
+
|
|
4
|
+
Writes only docs/engineering/codebase-map.md and docs/engineering/dependencies.json
|
|
5
|
+
per /map-codebase contract. Does not append docs/engineering/state.md.
|
|
6
|
+
|
|
7
|
+
Diagnostics on stdout use deterministic tokens: [CODEBASE_MAP_OK] ... and CODEBASE_MAP_BLOCKED:...
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import argparse
|
|
13
|
+
import json
|
|
14
|
+
import os
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
BOOTSTRAP_SENTINEL = "<!-- its-magic:codebase-map-bootstrap v1 -->"
|
|
18
|
+
|
|
19
|
+
REMEDIATION = (
|
|
20
|
+
"Remediation: run `/map-codebase` for a full pass; see "
|
|
21
|
+
"`docs/engineering/runbook.md` (Codebase map bootstrap) and "
|
|
22
|
+
"`docs/engineering/architecture.md` (# US-0082)."
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _bootstrap_map_markdown() -> str:
|
|
27
|
+
lines = [
|
|
28
|
+
"# Codebase Map",
|
|
29
|
+
"",
|
|
30
|
+
BOOTSTRAP_SENTINEL,
|
|
31
|
+
"",
|
|
32
|
+
"This is a **bootstrap** codebase map created by the lifecycle materializer "
|
|
33
|
+
"(**US-0082** / **DEC-0065**). It satisfies the “map exists” contract for fresh repos.",
|
|
34
|
+
"",
|
|
35
|
+
"For a full repository analysis, run **`/map-codebase`** (explicit/manual).",
|
|
36
|
+
"",
|
|
37
|
+
"## Stack",
|
|
38
|
+
"",
|
|
39
|
+
"| Aspect | Detail |",
|
|
40
|
+
"|--------|--------|",
|
|
41
|
+
"| (pending) | Run `/map-codebase` or edit this table after local analysis |",
|
|
42
|
+
"",
|
|
43
|
+
"## Entry Points",
|
|
44
|
+
"",
|
|
45
|
+
"| Entry | File | Purpose |",
|
|
46
|
+
"|-------|------|---------|",
|
|
47
|
+
"| (pending) | — | Run `/map-codebase` to populate |",
|
|
48
|
+
"",
|
|
49
|
+
"## Next steps",
|
|
50
|
+
"",
|
|
51
|
+
"1. Run **`/map-codebase`** for a deep pass, **or**",
|
|
52
|
+
"2. Edit this file directly with project-specific structure.",
|
|
53
|
+
"",
|
|
54
|
+
]
|
|
55
|
+
return "\n".join(lines)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _bootstrap_dependencies_obj() -> dict:
|
|
59
|
+
return {"libraries": [], "runtime": [], "tooling": []}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _bootstrap_dependencies_text() -> str:
|
|
63
|
+
return json.dumps(_bootstrap_dependencies_obj(), sort_keys=True, indent=2) + "\n"
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _normalize_newlines(text: str) -> str:
|
|
67
|
+
return text.replace("\r\n", "\n").replace("\r", "\n")
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _json_stable(obj: object) -> str:
|
|
71
|
+
return json.dumps(obj, sort_keys=True, separators=(",", ":"))
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def sync_map(map_path: Path, desired: str) -> tuple[str, bool]:
|
|
75
|
+
"""Returns (status_token, content_mutated)."""
|
|
76
|
+
if not map_path.is_file():
|
|
77
|
+
map_path.write_text(desired, encoding="utf-8", newline="\n")
|
|
78
|
+
return "created", True
|
|
79
|
+
existing = _normalize_newlines(map_path.read_text(encoding="utf-8"))
|
|
80
|
+
if BOOTSTRAP_SENTINEL in existing:
|
|
81
|
+
if existing == desired:
|
|
82
|
+
return "noop", False
|
|
83
|
+
map_path.write_text(desired, encoding="utf-8", newline="\n")
|
|
84
|
+
return "refreshed_bootstrap", True
|
|
85
|
+
return "preserved_existing", False
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def sync_dependencies(deps_path: Path, map_allows_bootstrap_deps: bool) -> tuple[str, bool]:
|
|
89
|
+
"""When map_allows_bootstrap_deps, deps may be created/updated to bootstrap empty schema."""
|
|
90
|
+
desired_txt = _bootstrap_dependencies_text()
|
|
91
|
+
want_obj = _bootstrap_dependencies_obj()
|
|
92
|
+
want_stable = _json_stable(want_obj)
|
|
93
|
+
|
|
94
|
+
if not deps_path.is_file():
|
|
95
|
+
deps_path.write_text(desired_txt, encoding="utf-8", newline="\n")
|
|
96
|
+
return "deps_created", True
|
|
97
|
+
|
|
98
|
+
raw = deps_path.read_text(encoding="utf-8")
|
|
99
|
+
try:
|
|
100
|
+
cur = json.loads(raw)
|
|
101
|
+
except json.JSONDecodeError:
|
|
102
|
+
if map_allows_bootstrap_deps:
|
|
103
|
+
deps_path.write_text(desired_txt, encoding="utf-8", newline="\n")
|
|
104
|
+
return "deps_repaired", True
|
|
105
|
+
return "deps_preserved_existing", False
|
|
106
|
+
|
|
107
|
+
if _json_stable(cur) == want_stable:
|
|
108
|
+
return "deps_noop", False
|
|
109
|
+
|
|
110
|
+
if map_allows_bootstrap_deps:
|
|
111
|
+
deps_path.write_text(desired_txt, encoding="utf-8", newline="\n")
|
|
112
|
+
return "deps_refreshed", True
|
|
113
|
+
return "deps_preserved_existing", False
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def main(argv: list[str] | None = None) -> int:
|
|
117
|
+
p = argparse.ArgumentParser(description="Materialize idempotent codebase map bootstrap.")
|
|
118
|
+
p.add_argument("--repo", default=".", help="Repository root")
|
|
119
|
+
p.add_argument(
|
|
120
|
+
"--trigger",
|
|
121
|
+
choices=("architecture", "map-codebase", "refresh-context"),
|
|
122
|
+
default="architecture",
|
|
123
|
+
help="Lifecycle trigger label (stdout diagnostics only)",
|
|
124
|
+
)
|
|
125
|
+
p.add_argument("--dry-run", action="store_true", help="Print actions; do not write files")
|
|
126
|
+
p.add_argument(
|
|
127
|
+
"--simulate-block",
|
|
128
|
+
metavar="SUBREASON",
|
|
129
|
+
default=None,
|
|
130
|
+
help="Emit CODEBASE_MAP_BLOCKED:SUBREASON and exit 2 (tests)",
|
|
131
|
+
)
|
|
132
|
+
p.add_argument(
|
|
133
|
+
"--check-present",
|
|
134
|
+
action="store_true",
|
|
135
|
+
help="Read-only: exit 0 if codebase-map.md exists, else CODEBASE_MAP_MISSING + exit 2",
|
|
136
|
+
)
|
|
137
|
+
args = p.parse_args(argv)
|
|
138
|
+
|
|
139
|
+
if args.simulate_block:
|
|
140
|
+
print(f"[CODEBASE_MAP_BLOCKED:{args.simulate_block}] trigger={args.trigger}")
|
|
141
|
+
print(REMEDIATION)
|
|
142
|
+
return 2
|
|
143
|
+
|
|
144
|
+
if os.environ.get("CODEBASE_MAP_LIFECYCLE_SKIP", "").strip() in ("1", "true", "yes"):
|
|
145
|
+
print(f"[CODEBASE_MAP_BLOCKED:policy_skip] trigger={args.trigger}")
|
|
146
|
+
print(REMEDIATION)
|
|
147
|
+
return 2
|
|
148
|
+
|
|
149
|
+
root = Path(args.repo).resolve()
|
|
150
|
+
eng = root / "docs" / "engineering"
|
|
151
|
+
map_path = eng / "codebase-map.md"
|
|
152
|
+
deps_path = eng / "dependencies.json"
|
|
153
|
+
|
|
154
|
+
if args.check_present:
|
|
155
|
+
if map_path.is_file():
|
|
156
|
+
print(f"[CODEBASE_MAP_OK] check_present trigger={args.trigger} path={map_path}")
|
|
157
|
+
return 0
|
|
158
|
+
print("[CODEBASE_MAP_MISSING]")
|
|
159
|
+
print(REMEDIATION)
|
|
160
|
+
return 2
|
|
161
|
+
|
|
162
|
+
desired_map = _bootstrap_map_markdown()
|
|
163
|
+
|
|
164
|
+
if args.dry_run:
|
|
165
|
+
print(f"[CODEBASE_MAP_OK] dry_run trigger={args.trigger} map={map_path} deps={deps_path}")
|
|
166
|
+
return 0
|
|
167
|
+
|
|
168
|
+
eng.mkdir(parents=True, exist_ok=True)
|
|
169
|
+
|
|
170
|
+
map_status, _ = sync_map(map_path, desired_map)
|
|
171
|
+
print(f"[CODEBASE_MAP_OK] {map_status} trigger={args.trigger} path={map_path}")
|
|
172
|
+
|
|
173
|
+
map_text = _normalize_newlines(map_path.read_text(encoding="utf-8"))
|
|
174
|
+
map_allows_bootstrap_deps = BOOTSTRAP_SENTINEL in map_text
|
|
175
|
+
|
|
176
|
+
dep_status, _ = sync_dependencies(deps_path, map_allows_bootstrap_deps)
|
|
177
|
+
if dep_status not in ("deps_noop", "deps_preserved_existing"):
|
|
178
|
+
print(f"[CODEBASE_MAP_OK] {dep_status} trigger={args.trigger} path={deps_path}")
|
|
179
|
+
|
|
180
|
+
return 0
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
if __name__ == "__main__":
|
|
184
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Validate work/<story_id>/pack.json schema v1 (US-0096 / DEC-0082)."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
import json
|
|
8
|
+
import sys
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
REQUIRED_FIELDS = (
|
|
13
|
+
"schema_version",
|
|
14
|
+
"story_id",
|
|
15
|
+
"delivery_mode",
|
|
16
|
+
"status",
|
|
17
|
+
"ac",
|
|
18
|
+
"tasks",
|
|
19
|
+
"refs",
|
|
20
|
+
"deltas",
|
|
21
|
+
"memory_layer",
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
VALID_DELIVERY_MODES = frozenset({"standard", "ultra_lean", "mega_quick"})
|
|
25
|
+
VALID_MEMORY_LAYER = "pack"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def validate_pack(data: Any, *, expected_story_id: str | None = None) -> list[str]:
|
|
29
|
+
"""Return PACK_* reason codes; empty list means valid."""
|
|
30
|
+
codes: list[str] = []
|
|
31
|
+
if not isinstance(data, dict):
|
|
32
|
+
return ["PACK_INVALID_ROOT"]
|
|
33
|
+
|
|
34
|
+
for field in REQUIRED_FIELDS:
|
|
35
|
+
if field not in data:
|
|
36
|
+
codes.append(f"PACK_MISSING_REQUIRED_FIELD:{field}")
|
|
37
|
+
|
|
38
|
+
if codes:
|
|
39
|
+
return codes
|
|
40
|
+
|
|
41
|
+
if data.get("schema_version") != "1":
|
|
42
|
+
codes.append("PACK_SCHEMA_VERSION_INVALID")
|
|
43
|
+
|
|
44
|
+
story_id = data.get("story_id")
|
|
45
|
+
if not isinstance(story_id, str) or not story_id.strip():
|
|
46
|
+
codes.append("PACK_STORY_ID_INVALID")
|
|
47
|
+
elif expected_story_id and story_id != expected_story_id:
|
|
48
|
+
codes.append("PACK_STORY_ID_MISMATCH")
|
|
49
|
+
|
|
50
|
+
delivery_mode = data.get("delivery_mode")
|
|
51
|
+
if delivery_mode not in VALID_DELIVERY_MODES:
|
|
52
|
+
codes.append("PACK_DELIVERY_MODE_INVALID")
|
|
53
|
+
|
|
54
|
+
if not isinstance(data.get("status"), str) or not str(data["status"]).strip():
|
|
55
|
+
codes.append("PACK_STATUS_INVALID")
|
|
56
|
+
|
|
57
|
+
for list_field in ("ac", "tasks", "refs", "deltas"):
|
|
58
|
+
if not isinstance(data.get(list_field), list):
|
|
59
|
+
codes.append(f"PACK_INVALID_FIELD_TYPE:{list_field}")
|
|
60
|
+
|
|
61
|
+
if data.get("memory_layer") != VALID_MEMORY_LAYER:
|
|
62
|
+
codes.append("PACK_MEMORY_LAYER_INVALID")
|
|
63
|
+
|
|
64
|
+
return codes
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def main() -> int:
|
|
68
|
+
p = argparse.ArgumentParser(description="Validate pack.json schema v1 (US-0096).")
|
|
69
|
+
p.add_argument("--file", type=Path, help="Path to pack.json")
|
|
70
|
+
p.add_argument(
|
|
71
|
+
"--story-id",
|
|
72
|
+
help="Expected story_id (e.g. US-0096); mismatch → PACK_STORY_ID_MISMATCH",
|
|
73
|
+
)
|
|
74
|
+
p.add_argument(
|
|
75
|
+
"--self-test",
|
|
76
|
+
action="store_true",
|
|
77
|
+
help="Run built-in fixture checks and exit.",
|
|
78
|
+
)
|
|
79
|
+
args = p.parse_args()
|
|
80
|
+
|
|
81
|
+
if args.self_test:
|
|
82
|
+
valid = {
|
|
83
|
+
"schema_version": "1",
|
|
84
|
+
"story_id": "US-0096",
|
|
85
|
+
"delivery_mode": "ultra_lean",
|
|
86
|
+
"status": "OPEN",
|
|
87
|
+
"ac": ["AC-1"],
|
|
88
|
+
"tasks": [{"id": "T-001", "status": "pending"}],
|
|
89
|
+
"refs": ["decisions/DEC-0082.md"],
|
|
90
|
+
"deltas": [],
|
|
91
|
+
"memory_layer": "pack",
|
|
92
|
+
}
|
|
93
|
+
if validate_pack(valid):
|
|
94
|
+
print("PACK_SELF_TEST_FAIL: valid fixture rejected", file=sys.stderr)
|
|
95
|
+
return 1
|
|
96
|
+
invalid = dict(valid)
|
|
97
|
+
del invalid["story_id"]
|
|
98
|
+
if "PACK_MISSING_REQUIRED_FIELD:story_id" not in validate_pack(invalid):
|
|
99
|
+
print("PACK_SELF_TEST_FAIL: missing field not detected", file=sys.stderr)
|
|
100
|
+
return 1
|
|
101
|
+
print("[PACK_JSON_SELF_TEST_OK]")
|
|
102
|
+
return 0
|
|
103
|
+
|
|
104
|
+
if not args.file:
|
|
105
|
+
print("error: specify --file or --self-test", file=sys.stderr)
|
|
106
|
+
return 2
|
|
107
|
+
|
|
108
|
+
path = args.file.resolve()
|
|
109
|
+
if not path.is_file():
|
|
110
|
+
print(f"PACK_FILE_NOT_FOUND: {path}", file=sys.stderr)
|
|
111
|
+
return 2
|
|
112
|
+
|
|
113
|
+
try:
|
|
114
|
+
data = json.loads(path.read_text(encoding="utf-8"))
|
|
115
|
+
except json.JSONDecodeError as exc:
|
|
116
|
+
print(f"PACK_INVALID_JSON: {exc}", file=sys.stderr)
|
|
117
|
+
return 1
|
|
118
|
+
|
|
119
|
+
codes = validate_pack(data, expected_story_id=args.story_id)
|
|
120
|
+
if codes:
|
|
121
|
+
for code in codes:
|
|
122
|
+
print(code, file=sys.stderr)
|
|
123
|
+
return 1
|
|
124
|
+
|
|
125
|
+
print("[PACK_JSON_VALIDATION_OK]")
|
|
126
|
+
return 0
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
if __name__ == "__main__":
|
|
130
|
+
raise SystemExit(main())
|