okf-cli 0.8.0.0 → 0.9.0.0
raw patch · 5 files changed
+96/−25 lines, 5 filesdep ~okf-corePVP ok
version bump matches the API change (PVP)
Dependency ranges changed: okf-core
API changes (from Hackage documentation)
Files
- CHANGELOG.md +20/−0
- help/profiles.md +29/−8
- okf-cli.cabal +3/−3
- src/Okf/Cli.hs +18/−6
- test/Main.hs +26/−8
CHANGELOG.md view
@@ -7,6 +7,26 @@ ## [Unreleased] +## [0.9.0.0] - 2026-09-13++### Added++- `okf profile show` renders optional profile and type guidance as stable+ multiline blocks, and `--json` exposes the same string-or-null fields.+ `okf profile document` renders profile-wide guidance before additive+ type-specific guidance in Markdown bodies and never executes the prose.++### Changed++- **Breaking:** requires the coordinated `okf-core` release whose public+ profile schema and Haskell records add `guidance`; older descriptor values+ remain accepted by okf-core's frozen compatibility decoder.+- Requires `okf-core ^>=0.9.0.0`.+- `okf profile list` and `okf profile sources` report the built-in+ `mori://shinzui/okf-profiles` v0.14.0 catalogue (thirteen profiles, up from+ ten), including `assurance.reviews`, `assurance.failureModes`, and+ `documentation.userDocumentation`.+ ## [0.8.0.0] - 2026-08-19 ### Added
help/profiles.md view
@@ -140,9 +140,16 @@ leaves you to compose them. Recommended keys carry a bullet saying they are checked only under --strict. + Profile and type rules may also carry multiline `guidance`. The root page+ renders profile guidance. Every applicable type page renders a Guidance+ section with Profile-wide prose first and Type-specific prose second; a type+ rule adds to, never replaces, the universal instruction. Blank or absent+ guidance creates no section, and guidance remains body Markdown rather than+ generated frontmatter.+ The `description` prose you write on the profile, on a type rule, and on- each key is what fills the generated pages. A profile with no descriptions- still generates, with synthesized summaries.+ each key identifies what it documents. A profile with no descriptions still+ generates, with synthesized summaries. The output is an ordinary OKF bundle, so okf validate, okf graph, and okf show all work on it. Generation never reads the clock, so regenerating@@ -179,14 +186,27 @@ what a generated documentation bundle looks like, and examples/postgresql-profile/ as a committed worked example. -DESCRIPTIONS+DESCRIPTIONS, GUIDANCE, AND RULES A profile may document itself: one description for the profile as a whole, one per required, recommended, or optional frontmatter key, and one per type rule.- Descriptions are prose for humans -- okf never checks one against a bundle- and none can produce a deviation.+ A description says what the profile, type, or key is and stays concise enough+ for listings and generated frontmatter. + `guidance : Optional Text` is a separate multiline Markdown field on the+ profile and every type rule. It says how an author should work. Profile+ guidance applies to every type, and matching type guidance is added after it.+ `okf profile show` prints each block after its description, preserving+ internal lines; absent or whitespace-only values print `guidance: (none)`.+ Full show JSON includes string-or-null guidance fields, while compact profile+ listings omit them.++ Structured rules say what okf can actually check. Descriptions and guidance+ are prose for humans and tools -- neither can produce a deviation. okf never+ runs a command from guidance, invokes Hurl, queries an event stream, or+ executes a fenced code block.+ `okf profile list` shows the profile's own description on the indented line below its identity, reading "-" when it has none. `okf profile show` prints the profile description under the name, one " - key: prose" line per@@ -196,8 +216,9 @@ profile: schemas/sales/tables/orders: missing profile-required field: title (Human-readable name of the object.) - Descriptions are optional and additive. A descriptor written before they- existed loads unchanged and simply shows none; nothing needs migrating. See+ Descriptions and guidance are optional and additive. A descriptor written+ before either schema generation loads through its frozen compatibility+ decoder and simply shows none; nothing needs migrating. See docs/user/profiles.md for how to add them to a descriptor you already have. TYPE-AWARE FRONTMATTER@@ -484,7 +505,7 @@ runs are offline. Pass --registry with a local checkout to be offline throughout. - The built-in pin currently targets v0.10.0 and publishes ten OKF 0.2 profiles+ The built-in pin currently targets v0.14.0 and publishes thirteen OKF 0.2 profiles with descriptions. `okf profile sources` reports that version without network access. Pass --check-latest to that command for an explicit upstream tag comparison; a failed optional check is reported but does not change the source
okf-cli.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.4 name: okf-cli-version: 0.8.0.0+version: 0.9.0.0 synopsis: Command-line interface for Open Knowledge Format bundles description: okf-cli provides the @okf@ executable for working with Open Knowledge Format@@ -71,7 +71,7 @@ , generic-lens >=2.2 && <2.4 , githash ^>=0.1 , lens ^>=5.3- , okf-core ^>=0.8.0.0+ , okf-core ^>=0.9.0.0 , optparse-applicative >=0.18 && <0.20 , process >=1.6 && <1.7 , text ^>=2.1@@ -88,7 +88,7 @@ , directory , filepath , okf-cli- , okf-core ^>=0.8.0.0+ , okf-core ^>=0.9.0.0 , optparse-applicative >=0.18 , temporary , text ^>=2.1
src/Okf/Cli.hs view
@@ -1861,6 +1861,7 @@ ProfileSpec { name, description,+ guidance, okfVersion, frontmatter, allowUnknownTypes,@@ -1871,13 +1872,15 @@ } = [ "export: " <> displayExport exportPath, "name: " <> name,- "description: " <> renderOptional description,- "okfVersion: " <> okfVersion,- "requireBundleVersion: " <> renderOptional requireBundleVersion,- "allowUnknownTypes: " <> renderFlag allowUnknownTypes,- "allowUnknownFields: " <> renderFlag allowUnknownFields,- "idField: " <> renderOptional idField+ "description: " <> renderOptional description ]+ <> renderGuidance "" guidance+ <> [ "okfVersion: " <> okfVersion,+ "requireBundleVersion: " <> renderOptional requireBundleVersion,+ "allowUnknownTypes: " <> renderFlag allowUnknownTypes,+ "allowUnknownFields: " <> renderFlag allowUnknownFields,+ "idField: " <> renderOptional idField+ ] <> renderPresenceLists "" frontmatter <> concatMap renderTypeRule typeRules where@@ -1943,6 +1946,7 @@ TypeRule { type_ = ruleType, description = ruleDescription,+ guidance = ruleGuidance, frontmatter = typeFrontmatter, pathPattern, resourceScheme,@@ -1954,6 +1958,7 @@ "type: " <> ruleType, " description: " <> renderOptional ruleDescription ]+ <> renderGuidance " " ruleGuidance <> renderPresenceLists " " typeFrontmatter <> [ " pathPattern: " <> renderOptional pathPattern, " resourceScheme: " <> renderOptional resourceScheme,@@ -1964,6 +1969,13 @@ renderFlag True = "true" renderFlag False = "false"+ renderGuidance indent prose =+ case trimOuterBlankLines (maybe [] Text.lines prose) of+ [] -> [indent <> "guidance: (none)"]+ proseLines ->+ (indent <> "guidance:") : map (indent <>) (map (" " <>) proseLines)+ trimOuterBlankLines = List.dropWhileEnd isBlank . dropWhile isBlank+ isBlank = Text.null . Text.strip renderOptional = fromMaybe "(none)" renderList [] = "(none)" renderList values = Text.intercalate ", " values
test/Main.hs view
@@ -968,18 +968,18 @@ let rendered = renderProfileSourceResolution sampleProfileSourceResolutions FreshnessNotChecked in all (`Text.isInfixOf` rendered)- [ "okf-profiles v0.10.0 (pinned)",+ [ "okf-profiles v0.14.0 (pinned)", "[built-in default]", "[env: OKF_PROFILE_REGISTRIES]", "failed (import-failure)",- "Pinned catalogue: okf-profiles v0.10.0",+ "Pinned catalogue: okf-profiles v0.14.0", "1. --registry flag (repeatable)", "lookup fails closed." ] testProfileSourcesJsonShape :: Bool testProfileSourcesJsonShape =- profileSourcesJson sampleProfileSourceResolutions (FreshnessOutdated "v0.10.0" "v0.11.0")+ profileSourcesJson sampleProfileSourceResolutions (FreshnessOutdated "v0.14.0" "v0.15.0") == Aeson.object [ "sources" Aeson..= [ Aeson.object@@ -1009,13 +1009,13 @@ ] ] ],- "pinnedVersion" Aeson..= Just ("v0.10.0" :: Text.Text),+ "pinnedVersion" Aeson..= Just ("v0.14.0" :: Text.Text), "freshness" Aeson..= Aeson.object [ "status" Aeson..= ("outdated" :: Text.Text),- "pinnedVersion" Aeson..= ("v0.10.0" :: Text.Text),- "latestVersion" Aeson..= ("v0.11.0" :: Text.Text),- "refreshCommand" Aeson..= ("scripts/refresh-default-registry.sh v0.11.0" :: Text.Text)+ "pinnedVersion" Aeson..= ("v0.14.0" :: Text.Text),+ "latestVersion" Aeson..= ("v0.15.0" :: Text.Text),+ "refreshCommand" Aeson..= ("scripts/refresh-default-registry.sh v0.15.0" :: Text.Text) ], "precedence" Aeson..= profileSourcePrecedenceFixture ]@@ -1040,7 +1040,7 @@ && parseReleaseVersionTag "v0.9.3" == Just (ReleaseVersion 0 9 3) && parseReleaseVersionTag "0.10.0" == Nothing && parseReleaseVersionTag "v0.10" == Nothing- && pinnedRegistryTag defaultRegistryReference == Just "v0.10.0"+ && pinnedRegistryTag defaultRegistryReference == Just "v0.14.0" && latestReleaseTag "aaa\trefs/tags/v0.9.3\nbbb\trefs/tags/not-a-version\nccc\trefs/tags/v0.10.0\n" == Just "v0.10.0"@@ -1081,6 +1081,7 @@ ProfileSpec { name = "shinzui-postgresql", description = Nothing,+ guidance = Nothing, okfVersion = "0.1", frontmatter = FrontmatterRules@@ -1096,6 +1097,7 @@ [ TypeRule { type_ = "PostgreSQL Table", description = Nothing,+ guidance = Nothing, frontmatter = FrontmatterRules {required = [], recommended = [], optional = []}, pathPattern = Just "schemas/*/tables/*", resourceScheme = Just "postgresql",@@ -1111,6 +1113,9 @@ ProfileSpec { name = "decisions", description = Just "How this team records architectural decisions.",+ guidance =+ Just+ "\nCapture the context and evidence for every decision.\n\nLink the implementation plan when one exists.\n", okfVersion = "0.1", frontmatter = FrontmatterRules@@ -1155,6 +1160,7 @@ [ TypeRule { type_ = "Decision Record", description = Just "One accepted decision, never edited after acceptance.",+ guidance = Just "Explain the trade-offs.\n\nRecord follow-up work.", frontmatter = FrontmatterRules { required = [FieldRule "owner" (Just "Person responsible for the decision.") [] Scalar (Just (DocumentHandle "USR")) Nothing Nothing Nothing Nothing Nothing Nothing],@@ -1186,6 +1192,7 @@ ProfileSpec { name = "nested", description = Nothing,+ guidance = Nothing, okfVersion = "0.1", frontmatter = FrontmatterRules@@ -1253,6 +1260,7 @@ [ "export: (root)", "name: nested", "description: (none)",+ "guidance: (none)", "okfVersion: 0.1", "requireBundleVersion: (none)", "allowUnknownTypes: true",@@ -1332,6 +1340,10 @@ [ "export: nested.decisions", "name: decisions", "description: How this team records architectural decisions.",+ "guidance:",+ " Capture the context and evidence for every decision.",+ " ",+ " Link the implementation plan when one exists.", "okfVersion: 0.1", "requireBundleVersion: (none)", "allowUnknownTypes: false",@@ -1373,6 +1385,10 @@ "", "type: Decision Record", " description: One accepted decision, never edited after acceptance.",+ " guidance:",+ " Explain the trade-offs.",+ " ",+ " Record follow-up work.", " frontmatter.required:", " - owner: Person responsible for the decision.", " allowedValues: (any)",@@ -1421,6 +1437,7 @@ [ "export: (root)", "name: shinzui-postgresql", "description: (none)",+ "guidance: (none)", "okfVersion: 0.1", "requireBundleVersion: (none)", "allowUnknownTypes: false",@@ -1452,6 +1469,7 @@ "", "type: PostgreSQL Table", " description: (none)",+ " guidance: (none)", " frontmatter.required: (none)", " frontmatter.recommended: (none)", " frontmatter.optional: (none)",