its-magic 0.1.2-9 → 0.1.2
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 +21 -0
- package/installer.ps1 +759 -0
- package/installer.py +981 -0
- package/installer.sh +649 -0
- package/package.json +18 -14
- package/scripts/check_intake_template_parity.py +284 -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 +505 -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/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 +2320 -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/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 +284 -0
- package/template/scripts/check_token_cost_parity.py +69 -0
- package/template/scripts/dev_environment_lib.py +463 -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/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/scripts/release-all.sh +0 -226
- 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())
|