From 962e472dde6bc42037f30bfefcdd9d78836062d8 Mon Sep 17 00:00:00 2001 From: Nick Anderson Date: Wed, 8 Jul 2026 15:55:57 -0500 Subject: [PATCH 1/4] Added timer_policy attribute to classes: promise reference (3.27) Documents the timer_policy attribute on classes: promises (CFE-4681), which controls whether a persistent class restarts its timer when rediscovered. On the 3.27 stream the default is absolute, preserving the historical behavior; the default changed to reset in 3.28.0. Ticket: CFE-4681 --- .../reference/promise-types/classes.markdown | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/content/reference/promise-types/classes.markdown b/content/reference/promise-types/classes.markdown index 9705f171b..4a7f6f091 100644 --- a/content/reference/promise-types/classes.markdown +++ b/content/reference/promise-types/classes.markdown @@ -301,6 +301,52 @@ classes: **See also:** [`persistence` classes attribute][classes#persistence], [`persist_time` in classes body][Promise types#persist_time] +### timer_policy + +**Description:** Determines whether a persistent class restarts its counter +when rediscovered. + +When a [`persistence`][classes#persistence] timer is set, `timer_policy` +controls what happens on later runs while the class is still defined (loaded +from the persistent store): + +- `reset` re-evaluates the promise and restarts the timer, so the class keeps + persisting for as long as the policy keeps running and the expression stays + true. +- `absolute` preserves the original expiry. While the class is already defined + the promise is skipped, so the class disappears once the original timer + elapses, regardless of how often the policy runs. + +This is the counterpart of `timer_policy` in the classes body, which has always +defaulted to `reset`. + +**Type:** (menu option) + +**Allowed input range:** `absolute|reset` + +**Default value:** absolute + +**Example:** + +```cf3 {skip TODO} +bundle common setclasses +{ + classes: + "cached_class" + expression => "any", + persistence => "120", + timer_policy => "reset"; +} +``` + +**History:** + +- Introduced in CFEngine 3.27.2. +- The default is `absolute`, preserving the historical persistent-class + behavior. The default changed to `reset` in 3.28.0. + +**See also:** [`persistence` classes attribute][classes#persistence], [`timer_policy` in classes body][Promise types#timer_policy], [`persist_time` in classes body][Promise types#persist_time] + ### not **Description:** Evaluate the negation of string expression in normal form From 1c7b77a0cbc61e1e6ddd6c1012c62105efc7044a Mon Sep 17 00:00:00 2001 From: Nick Anderson Date: Wed, 8 Jul 2026 16:22:30 -0500 Subject: [PATCH 2/4] Unfence timer_policy example; linter now recognizes the attribute cfengine CLI 0.18.0 ships a syntax-description.json that includes timer_policy on classes: promises, so the example no longer needs the {skip TODO} fence to pass cfengine dev lint-docs. Dropping it means the example is syntax-checked like the others. --- content/reference/promise-types/classes.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/reference/promise-types/classes.markdown b/content/reference/promise-types/classes.markdown index 4a7f6f091..5af7a28f3 100644 --- a/content/reference/promise-types/classes.markdown +++ b/content/reference/promise-types/classes.markdown @@ -328,7 +328,7 @@ defaulted to `reset`. **Example:** -```cf3 {skip TODO} +```cf3 bundle common setclasses { classes: From 8dfdc4152e6f21f351db93b4c66273af0be23433 Mon Sep 17 00:00:00 2001 From: Simon Halvorsen Date: Wed, 8 Jul 2026 13:53:28 +0200 Subject: [PATCH 3/4] Fix: Added missing semicolon and newline for cfengine-cli lint/format-docs Signed-off-by: Simon Halvorsen (cherry picked from commit ec725753e8b5037a6b31cc3401cf72a02af43c74) --- .../example-snippets/software_and_patch_installation_1_2_3.cf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/examples/example-snippets/software_and_patch_installation_1_2_3.cf b/content/examples/example-snippets/software_and_patch_installation_1_2_3.cf index 5001c29b6..b1d573540 100644 --- a/content/examples/example-snippets/software_and_patch_installation_1_2_3.cf +++ b/content/examples/example-snippets/software_and_patch_installation_1_2_3.cf @@ -2,7 +2,7 @@ body common control { bundlesequence => { "packages" }; - inputs => { "cfengine_stdlib.cf" } + inputs => { "cfengine_stdlib.cf" }; } bundle agent packages From 09129ecbda5844f09aa6118aaa0538a6cc889fe3 Mon Sep 17 00:00:00 2001 From: Nick Anderson Date: Fri, 10 Jul 2026 14:00:33 -0500 Subject: [PATCH 4/4] Fix: Add missing semicolons and closing braces in cf3 examples The Lint and format workflow installs the cfengine CLI unpinned, and 0.18.1 parse-checks cf3 code blocks more strictly than earlier versions. This surfaced pre-existing syntax omissions in seven example blocks that older CLIs tolerated, failing `cfengine dev lint-docs` on any 3.27 PR. Add the missing trailing semicolon or bundle-closing brace so the examples parse: - reference/components/cf-execd.markdown: schedule attribute - reference/components/cf-agent.markdown: copyfrom_restrict_keys slist - reference/promise-types/files/_index.markdown: file_result attribute - examples/tutorials/manage-ntp.markdown: ntp bundle - resources/additional-topics/distributed-scheduling.markdown: two workflow_one bundles - resources/additional-topics/security.markdown: change_management bundle Co-Authored-By: Claude Opus 4.8 (1M context) --- content/examples/tutorials/manage-ntp.markdown | 1 + content/reference/components/cf-agent.markdown | 2 +- content/reference/components/cf-execd.markdown | 2 +- content/reference/promise-types/files/_index.markdown | 2 +- .../resources/additional-topics/distributed-scheduling.markdown | 2 ++ content/resources/additional-topics/security.markdown | 1 + 6 files changed, 7 insertions(+), 3 deletions(-) diff --git a/content/examples/tutorials/manage-ntp.markdown b/content/examples/tutorials/manage-ntp.markdown index 82cf8e8f6..28c406f3c 100644 --- a/content/examples/tutorials/manage-ntp.markdown +++ b/content/examples/tutorials/manage-ntp.markdown @@ -618,6 +618,7 @@ bundle agent ntp "servers" slist => getvalues("def.ntp[config][servers]"), if => isvariable("def.ntp[config][servers]"); +} ``` Notice two promises were introduced, one setting `driftfile` to the value of `$(def.ntp[config][driftfile])` if it is defined and one setting servers to the list of values for `def.ntp[config][servers]` if it is defined. [Augments][Augments] allows for variables to be set in the _def_ bundle scope very early before policy is evaluated. diff --git a/content/reference/components/cf-agent.markdown b/content/reference/components/cf-agent.markdown index 0350880ef..7d81bae34 100644 --- a/content/reference/components/cf-agent.markdown +++ b/content/reference/components/cf-agent.markdown @@ -529,7 +529,7 @@ body agent control copyfrom_restrict_keys => { "SHA=6565a8e647e61e4a7ff2c709e0fe772acce2e45aaa294b2bb713de0ba5a6d8c3", "SHA=727dd7f6f8b2344c6d69cf1d3ed0446c0f9f095ce1a114481d691bf1cb2b300d", - } + }; } ``` diff --git a/content/reference/components/cf-execd.markdown b/content/reference/components/cf-execd.markdown index 6d68e81af..a5e7599eb 100644 --- a/content/reference/components/cf-execd.markdown +++ b/content/reference/components/cf-execd.markdown @@ -43,7 +43,7 @@ body executor control mailto => "cfengine@example.org"; mailfrom => "cfengine@$(host).example.org"; smtpserver => "localhost"; - schedule => { "Min00", "Min30" } + schedule => { "Min00", "Min30" }; } ``` diff --git a/content/reference/promise-types/files/_index.markdown b/content/reference/promise-types/files/_index.markdown index 78562a950..45db130c9 100644 --- a/content/reference/promise-types/files/_index.markdown +++ b/content/reference/promise-types/files/_index.markdown @@ -2063,7 +2063,7 @@ body file_select example { leaf_name => { "prog.pid", "prog.log" }; path_name => { "/etc/.*", "/var/run/.*" }; - file_result => "leaf_name.path_name" + file_result => "leaf_name.path_name"; } ``` diff --git a/content/resources/additional-topics/distributed-scheduling.markdown b/content/resources/additional-topics/distributed-scheduling.markdown index 4dafc1c8f..cc0749721 100644 --- a/content/resources/additional-topics/distributed-scheduling.markdown +++ b/content/resources/additional-topics/distributed-scheduling.markdown @@ -184,6 +184,7 @@ bundle agent workflow_one "/usr/local/bin/my_job", action => if_elapsed("100"), classes => state_repaired("did_my_job"); +} ``` ## Fancy distributed encapsulation @@ -366,6 +367,7 @@ bundle agent workflow_one "/usr/local/bin/my_job", action => if_elapsed("100"), classes => state_repaired("did_my_job"); +} ``` ## Self-healing workflows diff --git a/content/resources/additional-topics/security.markdown b/content/resources/additional-topics/security.markdown index 65939367c..295e55f66 100644 --- a/content/resources/additional-topics/security.markdown +++ b/content/resources/additional-topics/security.markdown @@ -450,6 +450,7 @@ bundle agent change_management ), depends_on => { "grant_hash_tables" }, action => neighbourwatch("File changes observed on $(neighbours)"); +} ``` It works by building a list of neighbours for each host. The function peers can